/**
 * eling(鹅岭)
 * version  0.0.1
 * 获取浏览器指纹
 * 通过收集多种浏览器特性生成低碰撞率的指纹
 */
const eling = (function (global) {
  // 只能在浏览器环境
  if (typeof global == 'undefined' || global != window || !global.document) {
    throw new Error("不支持的运行环境,请在浏览器中运行(Unsupported environment,Please run in browser)");
  }
  /**
    * 获取WebGL渲染器信息(不是渲染出的图像指纹)
    */
  function getWebGLInfo(): string {
    try {
      const canvas = document.createElement('canvas');
      const gl = canvas.getContext('webgl');
      if (!gl)
        return 'no-webgl';
      // @ts-ignore
      const renderer = gl.getExtension(gl.RENDERER) || gl.getExtension('WEBGL_debug_renderer_info');
      let glInfo = "unknown";
      if (renderer) {
        glInfo = `${gl.getParameter(renderer.UNMASKED_VENDOR_WEBGL)}|${gl.getParameter(renderer.UNMASKED_RENDERER_WEBGL)}`;
      }
      // 定义要收集的WebGL参数
      const spf = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
      const info = {
        extensions: gl.getSupportedExtensions(),
        version: gl.getParameter(gl.VERSION),
        renderer: gl.getParameter(gl.RENDERER),
        vendor: gl.getParameter(gl.VENDOR),
        maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
        maxRenderbufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE),
        shaderPrecision: spf ? spf.precision : 0,
        precisionFormatRange: spf ? `${spf.rangeMin}-${spf.rangeMax}` : '0',
        maxVertexAttribs: gl.getParameter(gl.MAX_VERTEX_ATTRIBS),
        glInfo
      }
      return getHash(JSON.stringify(info));
    } catch (e: unknown) {
      if (e instanceof Error) {
        return `webgl-error:${e.message}`;
      }
      return 'webgl-unknown-error';
    }
  }
  /**
   * 获取Canvas指纹
   */
  function generateCanvasDataHex(): string {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    if (!ctx) {
      return 'no-canvas';
    }
    canvas.style.width = '256px';
    canvas.style.height = '128px';
    // 绘制特定图形和文本
    ctx.textBaseline = 'top';
    ctx.font = '14px Arial';
    ctx.fillStyle = '#f60';
    ctx.fillRect(125, 1, 62, 20);
    ctx.fillStyle = '#069';
    ctx.fillText('abcdefghijklmnopqrstuvwxyz$#%&', 2, 15);
    ctx.font = '16px Arial';
    ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
    ctx.fillText('*&^%%$#@!qwertyuiopasdfghjklzxcvbnm', 4, 20);
    ctx.fillStyle = 'rgba(0, 21, 255, 0.5)';
    ctx.fillRect(112, 36, 45, 16);
    const dataUrl = canvas.toDataURL();
    canvas.remove(); // 清理canvas元素
    return getHash(dataUrl);
  }

  function getAudioInfo(): string {
    try {
      // 创建音频上下文
      const context = new AudioContext();
      // 测试音频处理能力
      const oscillator = context.createOscillator();
      const analyser = context.createAnalyser();
      const gainNode = context.createGain();
      // 收集音频信息(一些参数设置)
      const audio = {
        sampleRate: context.sampleRate,
        maxChannelCount: context.destination.maxChannelCount,
        baseLatency: context.baseLatency,
        outputLatency: context.outputLatency,
        workletSupport: typeof context.audioWorklet !== 'undefined',
        oscillatorType: oscillator.type,
        analyserFFTSize: analyser.fftSize,
        gainValue: gainNode.gain.value,
        audioBufferSupport: typeof context.createBuffer !== 'undefined',
        currentTime: context.currentTime,
        contextState: context.state || 'unknown'
      }
      // 清理资源
      oscillator.disconnect();
      analyser.disconnect();
      gainNode.disconnect();
      context.close();
      return getHash(JSON.stringify(audio));
    } catch (e: unknown) {
      if (e instanceof Error) {
        return `generatea audio fingerprint error:${e.message}`;
      }
      return 'audio-fingerprint-unknown-error';
    }
  }

  /**
   * 生成浏览器音频指纹
   * 利用不同浏览器/设备的音频处理差异生成唯一标识符
   * 返回Promise<string> 解析为16进制哈希字符串
   */
  async function generateAudioRenderingHex(): Promise<string> {
    // 计算均值
    function _calculateMean(data: Float32Array): number {
      return data.reduce((sum, x) => sum + x, 0) / data.length;
    }
    // 计算方差
    function _calculateVariance(data: Float32Array): number {
      const mean = _calculateMean(data);
      return data.reduce((sum, x) => sum + Math.pow(x - mean, 2), 0) / data.length;
    }
    // 计算过零率
    function _calculateZeroCrossing(data: Float32Array): number {
      let count = 0;
      for (let i = 1; i < data.length; i++) {
        const a = data[i];
        const b = data[i - 1];
        if (a * b < 0) count++; // 符号变化
      }
      return count / data.length;
    }
    const sampleRate = 44100;
    // 创建离线音频上下文（避免发出声音）
    const context = new window.OfflineAudioContext(1, sampleRate, sampleRate);

    // 创建音频节点链：振荡器 -> 压缩器 -> 分析器
    const oscillator = context.createOscillator();
    const compressor = context.createDynamicsCompressor();
    const analyser = context.createAnalyser();

    // 配置音频节点参数（使用非常规值增加差异性）
    oscillator.type = 'sine';
    oscillator.frequency.value = 10000;  // 高频更容易暴露差异
    compressor.threshold.value = -50;
    compressor.knee.value = 40;
    compressor.ratio.value = 20;
    compressor.attack.value = 0.1;
    compressor.release.value = 0.25;

    // 连接音频节点
    oscillator.connect(compressor);
    compressor.connect(analyser);
    analyser.connect(context.destination);

    // 启动音频渲染
    oscillator.start(0);
    const audioBuffer = await context.startRendering();

    // 获取渲染后的音频数据
    const channelData = audioBuffer.getChannelData(0);

    // 从不同位置采样关键点（增加信息量） 可能出现undefined
    const samplePoints = [
      channelData[200] || 0,    // 初始段
      channelData[500] || 0,    // 稳定段
      channelData[4400] || 0,   // 结束段
      channelData[22000] || 0  // 超长段（部分浏览器可能无数据）
    ];

    // 计算特征值（结合统计特征和特殊点）
    const features = [
      // 1. 数据有效性标记（处理不同长度）
      channelData.length,
      // 2. 特殊点采样
      ...samplePoints,
      // 3. 统计特征（均值和方差）
      _calculateMean(channelData),
      _calculateVariance(channelData),
      // 4. 过零率（信号特征）
      _calculateZeroCrossing(channelData.slice(0, 1000))
    ];
    oscillator.disconnect();
    compressor.disconnect();
    analyser.disconnect();
    // 转换为整数特征编码
    const intFeatures = features.map(f =>
      Math.floor(Math.abs(f) * 1e14)  // 放大浮点差异
    );
    return getHash(intFeatures);
  }
  /**
   * 字体枚举
   * 获取已安装字体列表
   * 目前没有好的的策略体现字体的差异性，或者本身差异不大，考虑进一步优化或舍弃
   */
  function getFontsInfo(): string {
    const fonts = new Set<string>([
      // 核心字体 - 不同系统和浏览器中差异较大的字体
      // Windows 特有字体
      "Calibri", "Cambria", "Consolas", "Corbel", "Segoe UI", "Microsoft YaHei",
      "DengXian", "SimHei", "SimSun", "KaiTi", "FangSong",
      // macOS 特有字体
      "SF Pro", "SF Mono", "Apple SD Gothic Neo", "PingFang SC", "Hiragino Sans", "Osaka",
      // Linux 特有字体
      "DejaVu Sans", "DejaVu Sans Mono", "Ubuntu", "Cantarell",
      // 常见但在不同系统中有差异的字体
      "Arial", "Times New Roman", "Courier New", "Verdana", "Georgia", "Comic Sans MS", "Impact",
      // 通用字体族
      "sans-serif", "sans-serif-thin", "serif", "monospace", "system-ui",
      // 中文字体
      "ARNO PRO", "中易宋体", "仿宋", "华文细黑", "宋体", "微软雅黑", "雅黑", "黑体",
      // 其他常见字体 - 随便列了一些，也不知道为什么
      "Agency FB", "Arabic Typesetting", "AvantGarde Bk BT", "Bahnschrift", "BankGothic Md BT",
      "Batang", "Bitstream Vera Sans Mono", "Book Antiqua", "Bookman Old Style", "Bradley Hand ITC",
      "Century", "Clarendon", "Constantia", "Courier", "EUROSTILE", "Ebrima", "Franklin Gothic",
      "Freestyle Script", "French Script MT", "Futura Bk BT", "GOTHAM", "Gabriola", "Gadugi",
      "Garamond", "Gill Sans MT", "HELV", "Haettenschweiler", "Helvetica",
      "Humanst521 BT", "Ink Free", "Javanese Text", "Juice ITC", "Kristen ITC", "Leelawadee",
      "Letter Gothic", "Levenim MT", "Lucida Bright", "MS PGothic", "MS UI Gothic",
      "Meiryo UI", "Menlo", "MingLiU-ExtB", "Minion Pro", "Monotype Corsiva", "PMingLiU", "SCRIPTINA",
      "Segoe UI Emoji", "Serifa", "Sitka Banner", "Staccato222 BT", "Sylfaen", "Symbol",
      "TRAJAN PRO", "Tahoma", "Tempus Sans ITC", "Trebuchet MS", "Univers CE 55 Medium", "Vrinda",
      "Webdings", "Yu Gothic", "ZWAdobeF"
    ]);
    /**
     * 检测浏览器是否支持指定字体
     * 提前准备测试元素和基准元素
     */
    const testElement = document.createElement('div');
    const baseElement = document.createElement('div');
    testElement.textContent = baseElement.textContent = "abcdefghijklmnopqrstuvwxyz";
    document.body.insertAdjacentElement('beforeend', testElement);
    document.body.insertAdjacentElement('beforeend', baseElement);
    const baseStyle = {
      position: 'absolute',
      left: '-9999px',
      fontSize: '24px',
      visibility: 'hidden',
      fontVariant: 'normal',
      fontStyle: 'normal',
      fontWeight: 'normal'
    };
    Object.assign(baseElement.style, baseStyle, {
      fontFamily: 'monospace'
    });
    /**
     * 检测浏览器是否支持指定字体
     * @param fontName 要检测的字体名称
     * @returns 返回布尔值表示是否支持
     */
    function _checkFontSupport(fontName: string): boolean {
      // 配置测试元素（使用目标字体+备用字体）
      Object.assign(testElement.style, baseStyle, {
        fontFamily: `'${fontName.replace(/'/g, '\\\'')}', 'monospace'`
      });
      // 获取渲染宽度
      const testWidth = testElement.clientWidth;
      const baseWidth = baseElement.clientWidth;
      // 比较宽度差异（允许1px误差）
      return Math.abs(testWidth - baseWidth) > 1;
    }
    const availableFonts = new Array<string>();
    fonts.forEach(font => {
      if (_checkFontSupport(font)) {
        availableFonts.push(font);
      }
    });
    // 清理DOM元素
    testElement.remove();
    baseElement.remove();
    return getHash(availableFonts.sort());
  }

  // WebGPU 支持的 WGSL 扩展列表
  function getWgslLanguageFeaturesInfo(): string {
    const arr = new Array<string>();
    const wgslFeatures = (navigator as any).gpu?.wgslLanguageFeatures;
    if (wgslFeatures && wgslFeatures.keys) {
      arr.push(...Array.from<string>(wgslFeatures.keys()))
    }
    return getHash(arr);
  }
  // 一些需要考虑兼容或者标记为弃用的信息
  function compatibleInfo(): string {
    const arr = new Array<string | number | boolean>();
    // 插件信息
    if (navigator.plugins) {
      arr.push(...Array.from(navigator.plugins).map(plugin => plugin.name));
    }
    // 媒体类型，支持的格式 与navigator.plugins对应
    if (navigator.mimeTypes) {
      arr.push(...Array.from(navigator.mimeTypes).map(mimeType => mimeType.type));
    }
    // 平台信息
    if (navigator.platform) {
      arr.push(navigator.platform);
    }
    // 浮点计算
    arr.push(
      Math.acos(0.123424234234234242),
      Math.acosh(1e208),
      Math.sinh(1),
      Math.cos(10.000000000123),
      Math.asin(0.123424234234234242),
      Math.asinh(1),
      Math.atanh(0.5),
      Math.atan(0.5),
      Math.sin(-1e300),
      Math.tan(-1e300),
    );
    arr.push(!!window.localStorage, !!window.sessionStorage, !!window.indexedDB, navigator.cookieEnabled);
    return getHash(arr.sort());
  }
  /**
   * 使用哈希字符串生成哈希值
   * @param data 要哈希的数据（字符串或数组）
   * @returns 返回哈希值（16进制字符串）
   */
  function getHash(data: string | Array<string | number | boolean>): string {
    if (Array.isArray(data)) {
      data = data.join('|');  // 如果是数组，转换为'|'分隔的字符串
    }
    // 使用统一的sha256算法
    // 在部分浏览器中，crypto.subtle.digest对https有效，在http中无效，需要使用考虑替代方案
    // if (window.crypto && window.crypto.subtle) {
    //   const encoder = new TextEncoder();
    //   const u8arr = encoder.encode(data);
    //   const buffer = await window.crypto.subtle.digest('SHA-256', u8arr);
    //   return Array.from(new Uint8Array(buffer)).map(b => b.toString(16).padStart(2, '0')).join('');
    // }
    // 自定义替代默认的hash算法
    return sha256(data);
  }
  /**
 * 计算 SHA-256 哈希值
 * @param message 输入字符串或字节数组
 * @returns 十六进制格式的 SHA-256 哈希值
 */
  function sha256(message: string | number[]): string {
    // SHA-256 常量
    const K: Uint32Array = new Uint32Array([
      0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
      0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
      0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
      0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
      0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
      0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
      0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
      0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
      0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
      0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
      0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
      0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
      0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
      0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
      0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
      0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
    ]);

    /**
     * 右旋转函数
     */
    function rotr(n: number, x: number): number {
      return (x >>> n) | (x << (32 - n));
    }

    /**
     * SHA-256 逻辑函数
     */
    function _sum0(x: number): number {
      return rotr(2, x) ^ rotr(13, x) ^ rotr(22, x);
    }

    function _sum1(x: number): number {
      return rotr(6, x) ^ rotr(11, x) ^ rotr(25, x);
    }

    function _sigma0(x: number): number {
      return rotr(7, x) ^ rotr(18, x) ^ (x >>> 3);
    }

    function _sigma1(x: number): number {
      return rotr(17, x) ^ rotr(19, x) ^ (x >>> 10);
    }

    function _ch(x: number, y: number, z: number): number {
      return (x & y) ^ (~x & z);
    }

    function _maj(x: number, y: number, z: number): number {
      return (x & y) ^ (x & z) ^ (y & z);
    }

    /**
     * 将字符串转换为 UTF-8 字节数组
     */
    function _stringToUTF8Bytes(str: string): number[] {
      const bytes: number[] = [];
      for (let i = 0; i < str.length; i++) {
        let code = str.charCodeAt(i);

        // 处理 UTF-16 代理对
        if (code > 0xd7ff && code < 0xe000) {
          if (i + 1 < str.length) {
            const extra = str.charCodeAt(i + 1);
            if ((extra & 0xfc00) === 0xdc00) {
              code = ((code & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
              i++;
            }
          }
        }

        // 编码为 UTF-8
        if (code < 0x80) {
          bytes.push(code);
        } else if (code < 0x800) {
          bytes.push(0xc0 | (code >> 6));
          bytes.push(0x80 | (code & 0x3f));
        } else if (code < 0x10000) {
          bytes.push(0xe0 | (code >> 12));
          bytes.push(0x80 | ((code >> 6) & 0x3f));
          bytes.push(0x80 | (code & 0x3f));
        } else {
          bytes.push(0xf0 | (code >> 18));
          bytes.push(0x80 | ((code >> 12) & 0x3f));
          bytes.push(0x80 | ((code >> 6) & 0x3f));
          bytes.push(0x80 | (code & 0x3f));
        }
      }
      return bytes;
    }

    /**
     * 将字节数组转换为十六进制字符串
     */
    function _bytesToHex(bytes: number[]): string {
      return bytes.map(b => b.toString(16).padStart(2, '0')).join('');
    }

    /**
     * 对消息进行填充
     */
    function _padMessage(message: number[]): number[] {
      const bitLength = message.length * 8;
      const padded = [...message];

      // 添加 1 位 (0x80 = 10000000)
      padded.push(0x80);

      // 填充 0 直到长度满足 (448 mod 512)
      // 注意：长度是以字节为单位计算的
      while ((padded.length % 64) !== 56) {
        padded.push(0x00);
      }

      // 添加 64 位的原始消息长度（大端序）
      // 使用 DataView 来正确处理 64 位整数
      const lengthBuffer = new ArrayBuffer(8);
      const lengthView = new DataView(lengthBuffer);

      // 由于 JavaScript 使用 64 位浮点数，我们需要分别设置高 32 位和低 32 位
      // 对于大多数情况，我们只需要低 32 位
      lengthView.setUint32(0, 0, false); // 高 32 位设为 0
      lengthView.setUint32(4, bitLength, false); // 低 32 位设为消息长度

      for (let i = 0; i < 8; i++) {
        padded.push(lengthView.getUint8(i));
      }

      return padded;
    }

    /**
     * 将字节数组转换为 32 位字数组（大端序）
     */
    function _bytesToWords(bytes: number[]): Uint32Array {
      const words = new Uint32Array(16);
      for (let i = 0; i < 16; i++) {
        const offset = i * 4;
        words[i] = (bytes[offset] << 24) |
          (bytes[offset + 1] << 16) |
          (bytes[offset + 2] << 8) |
          (bytes[offset + 3]);
      }
      return words;
    }

    // 主逻辑开始
    let bytes: number[];

    if (typeof message === 'string') {
      bytes = _stringToUTF8Bytes(message);
    } else {
      bytes = message;
    }

    // 填充消息
    const padded = _padMessage(bytes);

    // 初始化哈希值
    let h0 = 0x6a09e667;
    let h1 = 0xbb67ae85;
    let h2 = 0x3c6ef372;
    let h3 = 0xa54ff53a;
    let h4 = 0x510e527f;
    let h5 = 0x9b05688c;
    let h6 = 0x1f83d9ab;
    let h7 = 0x5be0cd19;

    // 处理每个 512 位块（64 字节）
    for (let i = 0; i < padded.length; i += 64) {
      const block = padded.slice(i, i + 64);
      const words = _bytesToWords(block);

      // 扩展消息调度表
      const w = new Uint32Array(64);
      for (let t = 0; t < 16; t++) {
        w[t] = words[t];
      }
      for (let t = 16; t < 64; t++) {
        w[t] = (_sigma1(w[t - 2]) + w[t - 7] + _sigma0(w[t - 15]) + w[t - 16]) >>> 0;
      }

      // 初始化工作变量
      let a = h0;
      let b = h1;
      let c = h2;
      let d = h3;
      let e = h4;
      let f = h5;
      let g = h6;
      let h = h7;

      // 主循环
      for (let t = 0; t < 64; t++) {
        const T1 = (h + _sum1(e) + _ch(e, f, g) + K[t] + w[t]) >>> 0;
        const T2 = (_sum0(a) + _maj(a, b, c)) >>> 0;

        h = g;
        g = f;
        f = e;
        e = (d + T1) >>> 0;
        d = c;
        c = b;
        b = a;
        a = (T1 + T2) >>> 0;
      }

      // 计算中间哈希值
      h0 = (h0 + a) >>> 0;
      h1 = (h1 + b) >>> 0;
      h2 = (h2 + c) >>> 0;
      h3 = (h3 + d) >>> 0;
      h4 = (h4 + e) >>> 0;
      h5 = (h5 + f) >>> 0;
      h6 = (h6 + g) >>> 0;
      h7 = (h7 + h) >>> 0;
    }

    // 将最终哈希值转换为字节数组（大端序）
    const hashBytes: number[] = [];
    const hashWords = [h0, h1, h2, h3, h4, h5, h6, h7];

    for (const word of hashWords) {
      hashBytes.push(
        (word >>> 24) & 0xff,
        (word >>> 16) & 0xff,
        (word >>> 8) & 0xff,
        word & 0xff
      );
    }
    return _bytesToHex(hashBytes);
  }

  async function fingerprint(): Promise<string> {
    // 收集各种浏览器特性
    // 如果不增加新的特性，不要改变元素顺序
    const fp = [
      // 避免浏览器升级的影响,去掉版本号，保留系统和平台信息
      navigator.userAgent.replace(/\/[\d.]+/ig, "").replace(/\s+/g, ""),
      // 屏幕分辨率
      `${window.screen.width}x${window.screen.height}`,
      // 颜色深度
      window.screen.colorDepth,
      // 设备像素比
      window.devicePixelRatio,
      // 时区
      Intl.DateTimeFormat().resolvedOptions().timeZone,
      // 语言 - 部分浏览器隐私模式下，navigator.languages只显示首选语言，不显示所有支持的语言，与普通情况回不一致
      navigator.language,
      // 硬件并发数
      navigator.hardwareConcurrency,
      // 可用内存
      (navigator as any).deviceMemory || 0,
      // 最大触摸点
      navigator.maxTouchPoints || 0,
      // 音频渲染的特征
      await generateAudioRenderingHex(),
      // WebGPU 支持的 WGSL 扩展信息
      getWgslLanguageFeaturesInfo(),
      // WebGL 信息
      getWebGLInfo(),
      // 音频信息
      getAudioInfo(),
      // 字体信息
      getFontsInfo(),
      // 画布特征
      generateCanvasDataHex(),
      // 其他信息
      compatibleInfo()
    ];
    return getHash(fp);
  }
  return fingerprint;
})(globalThis);
// 导出函数,可根据需要取消注释
// export default eling;